package com.zzw.bridge.security.filter;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import com.zzw.bridge.common.exception.ErrorCode;
import com.zzw.bridge.security.cache.TokenStoreCache;
import com.zzw.bridge.security.user.UserDetail;
import com.zzw.bridge.common.utils.Result;
import com.zzw.bridge.security.utils.ResponseUtil;
import com.zzw.bridge.security.utils.TokenUtils;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Security认证过滤器配置
 *
 * @author XiaoJie
 * @date 2025/5/15 14:07
 */
@Component
@AllArgsConstructor
public class AuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * 直接放行的接口
     * /system/auth/login 登录接口
     * /doc.html、/favicon.ico、/webjars/**、/v3/** 为springdoc所需路径
     */
    private static final String[] ALLOWED_PATHS = {"/system/auth/login", "/version", "/doc.html", "/favicon.ico", "/webjars/**", "/v3/**","/swagger-ui.html","/business/file/getDownload/**"};
    private final TokenStoreCache tokenStoreCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
        try {
            String contentType = request.getContentType();
            //如果匹配到允许的路径,则跳过自定义逻辑并继续过滤链
            String path = request.getRequestURI();
            AntPathMatcher pathMatcher = new AntPathMatcher();
            for (String allowedPath : ALLOWED_PATHS) {
                if (pathMatcher.match(allowedPath, path)) {
                    // 如果匹配到允许的路径，则跳过自定义逻辑并继续过滤链
                    BodyReaderHttpServletRequestWrapper bodyReaderHttpServletRequestWrapper = new BodyReaderHttpServletRequestWrapper(request);
                    chain.doFilter(bodyReaderHttpServletRequestWrapper, response);
                    return;
                }
            }
            //multipart/form-data类型
            if (CharSequenceUtil.isNotBlank(contentType) && contentType.contains(ContentType.MULTIPART.getValue())) {
                //spring中使用MultipartResolver处理文件上传，所以这里需要将其封装往后传递
                MultipartResolver resolver = new StandardServletMultipartResolver();
                MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
                String accessToken = TokenUtils.getAccessToken(request);
                //内部feign调用直接跳过
                String feignHeader = request.getHeader("x-from");
                if (StringUtils.isBlank(accessToken) && (StringUtils.isNotEmpty(feignHeader) && "feign".equals(feignHeader))) {
                    chain.doFilter(multipartRequest, response);
                    return;
                }
                // accessToken为空，表示未登录
                if (StringUtils.isBlank(accessToken)) {
                    // chain.doFilter(multipartRequest, response);
                    ResponseUtil.out(response, Result.error(ErrorCode.UNAUTHORIZED));
                    return;
                }
                UserDetail user = tokenStoreCache.getUser(accessToken);
                if (user == null) {
                    //redis没有用户就返回401 重新登录
                    ResponseUtil.out(response, Result.error(ErrorCode.UNAUTHORIZED));
                    return;
                }
                // 用户存在
                Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                // 新建 SecurityContext
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);
                chain.doFilter(multipartRequest, response);
//            } else if (request.getHeader(HttpHeaders.ACCEPT).contains(MediaType.TEXT_EVENT_STREAM_VALUE)) {
//                chain.doFilter(request, response);
//            }
            } else {
                //对于其他的情况，我们统一使用包装类，将请求流进行缓存到容器
                BodyReaderHttpServletRequestWrapper cachedBodyHttpServletRequest = new BodyReaderHttpServletRequestWrapper(request);
                String accessToken = TokenUtils.getAccessToken(request);
                //内部feign调用直接跳过
                String feignHeader = request.getHeader("x-from");
                if (StringUtils.isBlank(accessToken) && (StringUtils.isNotEmpty(feignHeader) && "feign".equals(feignHeader))) {
                    chain.doFilter(cachedBodyHttpServletRequest, response);
                    return;
                }
                // accessToken为空，表示未登录
                if (StringUtils.isBlank(accessToken)) {
                    // chain.doFilter(cachedBodyHttpServletRequest, response);
                    ResponseUtil.out(response, Result.error(ErrorCode.UNAUTHORIZED));
                    return;
                }
                // 获取登录用户信息
                UserDetail user = tokenStoreCache.getUser(accessToken);
                if (user == null) {
                    //redis没有用户就返回401 重新登录
                    ResponseUtil.out(response, Result.error(ErrorCode.UNAUTHORIZED));
                    return;
                }
                // 用户存在
                Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                // 新建 SecurityContext
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);
                chain.doFilter(cachedBodyHttpServletRequest, response);
            }
        } finally {
            //读取完 Response body 之后，通过这个设置回去，就可以使得接口调用者可以正常接收响应了，否则会产生空响应的情况
            //注意要在过滤器方法的最后调用
            responseWrapper.copyBodyToResponse();
        }
    }
}
